package com.seu.magicfilter.widget

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.SurfaceTexture
import android.graphics.SurfaceTexture.OnFrameAvailableListener
import android.hardware.Camera
import android.hardware.Camera.PictureCallback
import android.opengl.EGL14
import android.opengl.GLES20
import android.util.AttributeSet
import android.view.SurfaceHolder
import com.seu.magicfilter.camera.CameraEngine
import com.seu.magicfilter.encoder.video.TextureMovieEncoder
import com.seu.magicfilter.encoder.video.TextureMovieEncoder.EncoderConfig
import com.seu.magicfilter.filter.advanced.MagicBeautyFilter
import com.seu.magicfilter.filter.base.MagicCameraInputFilter
import com.seu.magicfilter.filter.helper.MagicFilterType
import com.seu.magicfilter.helper.SavePictureTask
import com.seu.magicfilter.utils.OpenGlUtils
import com.seu.magicfilter.utils.Rotation
import com.seu.magicfilter.utils.TextureRotationUtil
import com.seu.magicfilter.widget.base.MagicBaseView
import java.io.File
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.IntBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

class MagicCameraView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) : MagicBaseView(context, attrs) {
    private var cameraInputFilter: MagicCameraInputFilter? = null
    private var beautyFilter: MagicBeautyFilter? = null
    private var surfaceTexture: SurfaceTexture? = null
    private var recordingEnabled: Boolean
    private var recordingStatus: Int
    private var outputFile: File? = null
    override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
        super.onSurfaceCreated(gl, config)
        recordingEnabled = videoEncoder.isRecording
        recordingStatus = if (recordingEnabled) {
            RECORDING_RESUMED
        } else {
            RECORDING_OFF
        }
        if (cameraInputFilter == null) {
            cameraInputFilter = MagicCameraInputFilter()
        }
        cameraInputFilter!!.init()
        if (textureId == OpenGlUtils.NO_TEXTURE) {
            textureId = OpenGlUtils.getExternalOESTextureID()
            if (textureId != OpenGlUtils.NO_TEXTURE) {
                surfaceTexture = SurfaceTexture(textureId)
                surfaceTexture!!.setOnFrameAvailableListener(onFrameAvailableListener)
            }
        }
    }

    override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
        super.onSurfaceChanged(gl, width, height)
        openCamera()
    }

    override fun onDrawFrame(gl: GL10) {
        super.onDrawFrame(gl)
        if (surfaceTexture == null) return
        surfaceTexture!!.updateTexImage()
        if (recordingEnabled) { //开启录屏
            when (recordingStatus) {
                RECORDING_OFF -> {
                    val info = CameraEngine.getCameraInfo()
                    videoEncoder.setPreviewSize(info.previewWidth, info.pictureHeight)
                    videoEncoder.setTextureBuffer(gLTextureBuffer)
                    videoEncoder.setCubeBuffer(gLCubeBuffer)
                    //开始录制视频
                    outputFile?.let { file ->
                        videoEncoder.startRecording(EncoderConfig(
                                file, info.previewWidth, info.pictureHeight,
                                1000000, EGL14.eglGetCurrentContext(),
                                info))
                    }

                    recordingStatus = RECORDING_ON
                }
                RECORDING_RESUMED -> {
                    videoEncoder.updateSharedContext(EGL14.eglGetCurrentContext())
                    recordingStatus = RECORDING_ON
                }
                RECORDING_ON -> {
                }
                else -> throw RuntimeException("unknown status $recordingStatus")
            }
        } else {
            when (recordingStatus) {
                RECORDING_ON, RECORDING_RESUMED -> {
                    videoEncoder.stopRecording()
                    recordingStatus = RECORDING_OFF
                }
                RECORDING_OFF -> {
                }
                else -> throw RuntimeException("unknown status $recordingStatus")
            }
        }
        val mtx = FloatArray(16)
        surfaceTexture?.getTransformMatrix(mtx)
        cameraInputFilter?.setTextureTransformMatrix(mtx)
        var id = textureId
        if (filter == null) {
            cameraInputFilter?.onDrawFrame(textureId, gLCubeBuffer, gLTextureBuffer)
        } else {
            id = cameraInputFilter!!.onDrawToTexture(textureId)
            filter.onDrawFrame(id, gLCubeBuffer, gLTextureBuffer)
        }
        videoEncoder.setTextureId(id)//绑定
        videoEncoder.frameAvailable(surfaceTexture)//通知Encoder有新的一帧可用,如果当前在录制的话就会进行编码封装
    }

    private val onFrameAvailableListener = OnFrameAvailableListener { surfaceTexture: SurfaceTexture? -> requestRender() }
    override fun setFilter(type: MagicFilterType) {
        super.setFilter(type)
        videoEncoder.setFilter(type)
    }

    private fun openCamera() {
        if (CameraEngine.getCamera() == null) CameraEngine.openCamera()
        val info = CameraEngine.getCameraInfo()
        if (info.orientation == 90 || info.orientation == 270) {
            imageWidth = info.previewHeight
            imageHeight = info.previewWidth
        } else {
            imageWidth = info.previewWidth
            imageHeight = info.previewHeight
        }
        cameraInputFilter!!.onInputSizeChanged(imageWidth, imageHeight)
        adjustSize(info.orientation, info.isFront, true)
        if (surfaceTexture != null) CameraEngine.startPreview(surfaceTexture)
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        super.surfaceDestroyed(holder)
        CameraEngine.releaseCamera()
    }

    fun changeRecordingState(isRecording: Boolean) {
        recordingEnabled = isRecording
    }

    override fun onFilterChanged() {
        super.onFilterChanged()
        cameraInputFilter!!.onDisplaySizeChanged(surfaceWidth, surfaceHeight)
        if (filter != null) cameraInputFilter!!.initCameraFrameBuffer(imageWidth, imageHeight) else cameraInputFilter!!.destroyFramebuffers()
    }

    override fun savePicture(savePictureTask: SavePictureTask) {
        CameraEngine.takePicture(null, null, PictureCallback { data: ByteArray, camera: Camera? ->
            CameraEngine.stopPreview()
            val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
            queueEvent {
                var photo: Bitmap? = drawPhoto(bitmap, CameraEngine.getCameraInfo().isFront)
                GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight)
                if (pictureHandlerCall != null) {
                    photo = pictureHandlerCall?.preHandlerForSavePicture(photo)
                }
                if (photo != null) savePictureTask.execute(photo)
            }
            CameraEngine.startPreview()
        })
    }

    private fun drawPhoto(bitmap: Bitmap, isRotated: Boolean): Bitmap {
        val width = bitmap.width
        val height = bitmap.height
        val mFrameBuffers = IntArray(1)
        val mFrameBufferTextures = IntArray(1)
        if (beautyFilter == null) {
            beautyFilter = MagicBeautyFilter()
        }
        beautyFilter!!.init()
        beautyFilter!!.onDisplaySizeChanged(width, height)
        beautyFilter!!.onInputSizeChanged(width, height)
        if (filter != null) {
            filter.onInputSizeChanged(width, height)
            filter.onDisplaySizeChanged(width, height)
        }
        GLES20.glGenFramebuffers(1, mFrameBuffers, 0)
        GLES20.glGenTextures(1, mFrameBufferTextures, 0)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFrameBufferTextures[0])
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
                GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null)
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR.toFloat())
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE.toFloat())
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE.toFloat())
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0])
        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
                GLES20.GL_TEXTURE_2D, mFrameBufferTextures[0], 0)
        GLES20.glViewport(0, 0, width, height)
        val textureId = OpenGlUtils.loadTexture(bitmap, OpenGlUtils.NO_TEXTURE, true)
        val gLCubeBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.CUBE.size * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
        val gLTextureBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.TEXTURE_NO_ROTATION.size * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
        gLCubeBuffer.put(TextureRotationUtil.CUBE).position(0)
        if (isRotated) gLTextureBuffer.put(TextureRotationUtil.getRotation(Rotation.NORMAL, false, false)).position(0) else gLTextureBuffer.put(TextureRotationUtil.getRotation(Rotation.NORMAL, false, true)).position(0)
        if (filter == null) {
            beautyFilter!!.onDrawFrame(textureId, gLCubeBuffer, gLTextureBuffer)
        } else {
            beautyFilter!!.onDrawFrame(textureId)
            filter.onDrawFrame(mFrameBufferTextures[0], gLCubeBuffer, gLTextureBuffer)
        }
        val ib = IntBuffer.allocate(width * height)
        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib)
        val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        result.copyPixelsFromBuffer(ib)
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
        GLES20.glDeleteTextures(1, intArrayOf(textureId), 0)
        GLES20.glDeleteFramebuffers(mFrameBuffers.size, mFrameBuffers, 0)
        GLES20.glDeleteTextures(mFrameBufferTextures.size, mFrameBufferTextures, 0)
        beautyFilter!!.destroy()
        beautyFilter = null
        if (filter != null) {
            filter.onDisplaySizeChanged(surfaceWidth, surfaceHeight)
            filter.onInputSizeChanged(imageWidth, imageHeight)
        }
        return result
    }

    fun onBeautyLevelChanged() {
        cameraInputFilter!!.onBeautyLevelChanged()
    }

    fun setOutFile(outputMediaFile: File): MagicCameraView {
        this.outputFile = outputMediaFile
        return this;
    }

    var pictureHandlerCall: PictureHandlerCall? = null

    interface PictureHandlerCall {
        fun preHandlerForSavePicture(bitmap: Bitmap?): Bitmap?
    }

    companion object {
        private const val RECORDING_OFF = 0
        private const val RECORDING_ON = 1
        private const val RECORDING_RESUMED = 2
        private val videoEncoder = TextureMovieEncoder()
    }

    init {
        this.holder.addCallback(this)
        recordingStatus = -1
        recordingEnabled = false
        scaleType = ScaleType.CENTER_CROP
    }
}